#include <tinx/printk.h>
#include <tinx/interrupt.h>
#include <tinx/global.h>
#include <tinx/cpu.h>
#include <tinx/debug.h>
#include <tinx/string.h>

#define LOGK(str, args...) DEBUGK(str, ##args)

typedef struct descriptor_t
{
    u16 offset_low;
    u16 selector;
    u8 RESERVED;
    u8 type : 4;
    u8 RESERVED : 1;
    u8 dpl : 2;
    u8 present : 1;
    u16 offset_high;
} _packed descriptor_t;

extern handler_t handler_entry_table[0x30];
handler_t handler_table[0x30];
static descriptor_t idt[256];
static pointer_t idt_ptr;

static ic_op_t *ic_op;

static char *messages[] = {
    "#DE Divide Error\0",
    "#DB RESERVED\0",
    "--  NMI Interrupt\0",
    "#BP Breakpoint\0",
    "#OF Overflow\0",
    "#BR BOUND Range Exceeded\0",
    "#UD Invalid Opcode (Undefined Opcode)\0",
    "#NM Device Not Available (No Math Coprocessor)\0",
    "#DF Double Fault\0",
    "    Coprocessor Segment Overrun (reserved)\0",
    "#TS Invalid TSS\0",
    "#NP Segment Not Present\0",
    "#SS Stack-Segment Fault\0",
    "#GP General Protection\0",
    "#PF Page Fault\0",
    "--  (Intel reserved. Do not use.)\0",
    "#MF x87 FPU Floating-Point Error (Math Fault)\0",
    "#AC Alignment Check\0",
    "#MC Machine Check\0",
    "#XF SIMD Floating-Point Exception\0",
    "#VE Virtualization Exception\0",
    "#CP Control Protection Exception\0",
};

void ic_set_op(ic_op_t *op)
{
    ic_op = op;
}

void send_eoi(u8 vector)
{
    u8 irq = vector - 0x20;
    ic_op->send_eoi(irq);
}

void set_interrupt_mask(u8 irq, bool value)
{
    ic_op->set_mask(irq, value);
}

extern bool vm86_handler(intr_frame_t *iframe);

void intr_fault(intr_frame_t *iframe)
{
    char *message = NULL;
    if (iframe->vector < 22)
    {
        message = messages[iframe->vector];
    }
    else
    {
        message = messages[15];
    }

    task_t *task = running_task();

    printk("\nEXCEPTION : %s \n", message);
    printk("     TASK : %s\n", task->name);
    printk("   VECTOR : 0x%02X\n", iframe->vector);
    printk("    ERROR : 0x%08X\n", iframe->error);
    printk("   EFLAGS : 0x%08X\n", iframe->eflags);
    printk("       CS : 0x%02X\n", iframe->cs);
    printk("      EIP : 0x%08X\n", iframe->eip);
    printk("      ESP : 0x%08X\n", iframe->esp);
}

void default_handler(
    u32 vector, u32 edi, u32 esi, u32 ebp, u32 esp_dummy,
    u32 ebx, u32 edx, u32 ecx, u32 eax,
    u32 gs, u32 fs, u32 es, u32 ds,
    u32 vector0, u32 error, u32 eip, u32 cs, u32 eflags, u32 esp, u32 ss)
{
    if (vector >= 0x20)
    {
        send_eoi(vector);
        DEBUGK("interrupt vector 0x%x\n", vector);
        return;
    }

    intr_fault((intr_frame_t *)&vector);

    asm volatile(
        "cli\n"
        "hlt\n");
}

void ud_handler(
    u32 vector, u32 edi, u32 esi, u32 ebp, u32 esp_dummy,
    u32 ebx, u32 edx, u32 ecx, u32 eax,
    u32 gs, u32 fs, u32 es, u32 ds,
    u32 vector0, u32 error, u32 eip, u32 cs, u32 eflags, u32 esp, u32 ss)
{
    intr_fault((intr_frame_t *)&vector);
    printk("   OPCODE : 0x%08X\n", *(u32 *)eip);

    asm volatile(
        "cli\n"
        "hlt\n");
}

void gp_handler(
    u32 vector, u32 edi, u32 esi, u32 ebp, u32 esp_dummy,
    u32 ebx, u32 edx, u32 ecx, u32 eax,
    u32 gs, u32 fs, u32 es, u32 ds,
    u32 vector0, u32 error, u32 eip, u32 cs, u32 eflags, u32 esp, u32 ss)
{
    if (eflags & EFLAG_VM)
    {
        vm86_handler((intr_frame_t *)&vector);
    }
    else
    {
        intr_fault((intr_frame_t *)&vector);
        printk("   OPCODE : 0x%08X\n", *(u32 *)eip);

        asm volatile(
            "cli\n"
            "hlt\n");
    }
}

void set_interrupt_handler(u8 vector, handler_t handler)
{
    handler_table[vector] = handler;
}

// 清除 IF 位，返回设置之前的值
bool interrupt_disable()
{
    asm volatile(
        "pushfl\n"        // 将当前 eflags 压入栈中
        "cli\n"           // 清除 IF 位，此时外中断已被屏蔽
        "popl %eax\n"     // 将刚才压入的 eflags 弹出到 eax
        "shrl $9, %eax\n" // 将 eax 右移 9 位，得到 IF 位
        "andl $1, %eax\n" // 只需要 IF 位
    );
}

// 获得 IF 位
bool get_interrupt_state()
{
    asm volatile(
        "pushfl\n"        // 将当前 eflags 压入栈中
        "popl %eax\n"     // 将压入的 eflags 弹出到 eax
        "shrl $9, %eax\n" // 将 eax 右移 9 位，得到 IF 位
        "andl $1, %eax\n" // 只需要 IF 位
    );
}

// 设置 IF 位
void set_interrupt_state(bool state)
{
    if (state)
        asm volatile("sti\n");
    else
        asm volatile("cli\n");
}

extern void syscall_handler();
extern void page_fault();

void idt_init()
{
    memset(idt, 0, sizeof(idt));

    for (int i = 0; i < 0x30; i++)
    {
        handler_t handler = handler_entry_table[i];
        descriptor_t *desc = idt + i;
        desc->dpl = 0;
        desc->present = 1;
        desc->type = 0b1110; // 32 位中断门
        desc->selector = KERNEL_CODE_SELECTOR;
        desc->offset_low = (u32)handler & 0xffff;
        desc->offset_high = ((u32)handler >> 16) & 0xffff;
    }

    descriptor_t *desc = &idt[0x80];
    desc->dpl = 3; // 用户级也可以使用
    desc->offset_low = (u32)syscall_handler & 0xffff;
    desc->offset_high = ((u32)syscall_handler >> 16) & 0xffff;
    desc->selector = KERNEL_CODE_SELECTOR;
    desc->type = 0b1110;
    desc->present = 1;

    idt_ptr.offset = (u32)idt;
    idt_ptr.size = sizeof(idt) - 1;

    asm volatile("lidt idt_ptr\n");
}

extern void pic_init();

void interrupt_init()
{
    for (int i = 0; i < 0x30; i++)
    {
        handler_table[i] = default_handler;
    }

    set_interrupt_handler(INTR_GP, gp_handler);
    set_interrupt_handler(INTR_UD, ud_handler);
    set_interrupt_handler(INTR_PF, page_fault);

    idt_init();
    pic_init();
}